From: Keir Fraser Date: Tue, 3 Nov 2009 09:33:22 +0000 (+0000) Subject: x86: Clean up APIC local timer handling. X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~13146 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https:/%22bookmarks://%22Dat/%22http:/www.example.com/cgi/%22https:/%22bookmarks:/%22Dat?a=commitdiff_plain;h=b95beb185810484d5afe91b994d2c2d1670af74c;p=xen.git x86: Clean up APIC local timer handling. 1. Writing TMICT=0 disables the timer. Use this fact to simplify and improve reprogram_timer(). In particular, we always write TMICT, and write zero when we do not need a timer interrupt. 2. In HPET broadcast timer handler, set TMICT=0 when we mask the APIC local timer. May as well do this early, before entering deep sleep. 3. In HVM-guest APIC emulation, disable the emulated local timer when the guest sets TMICT=0. Previously we would issue an immediate one-shot interrupt. Signed-off-by: Keir Fraser --- diff --git a/xen/arch/x86/apic.c b/xen/arch/x86/apic.c index b9004413a8..64be332ca6 100644 --- a/xen/arch/x86/apic.c +++ b/xen/arch/x86/apic.c @@ -1178,65 +1178,25 @@ void enable_APIC_timer(void) #undef APIC_DIVISOR /* - * reprogram the APIC timer. Timeoutvalue is in ns from start of boot - * returns 1 on success - * returns 0 if the timeout value is too small or in the past. + * reprogram_timer: Reprogram the APIC timer. + * Timeout is a Xen system time (nanoseconds since boot); 0 disables the timer. + * Returns 1 on success; 0 if the timeout is too soon or is in the past. */ int reprogram_timer(s_time_t timeout) { - s_time_t now; - s_time_t expire; - u64 apic_tmict; + s_time_t expire; + u32 apic_tmict = 0; - /* - * If we don't have local APIC then we just poll the timer list off the - * PIT interrupt. - */ + /* No local APIC: timer list is polled via the PIT interrupt. */ if ( !cpu_has_apic ) return 1; - /* - * We use this value because we don't trust zero (we think it may just - * cause an immediate interrupt). At least this is guaranteed to hold it - * off for ages (esp. since the clock ticks on bus clock, not cpu clock!). - */ - if ( timeout == 0 ) - { - apic_tmict = 0xffffffff; - goto reprogram; - } - - now = NOW(); - expire = timeout - now; /* value from now */ - - if ( expire <= 0 ) - { - Dprintk("APICT[%02d] Timeout in the past 0x%08X%08X > 0x%08X%08X\n", - smp_processor_id(), (u32)(now>>32), - (u32)now, (u32)(timeout>>32),(u32)timeout); - return 0; - } - - /* conversion to bus units */ - apic_tmict = (((u64)bus_scale) * expire)>>18; - - if ( apic_tmict >= 0xffffffff ) - { - Dprintk("APICT[%02d] Timeout value too large\n", smp_processor_id()); - apic_tmict = 0xffffffff; - } - - if ( apic_tmict == 0 ) - { - Dprintk("APICT[%02d] timeout value too small\n", smp_processor_id()); - return 0; - } + if ( timeout && ((expire = timeout - NOW()) > 0) ) + apic_tmict = min_t(u64, (bus_scale * expire) >> 18, UINT_MAX); - reprogram: - /* Program the timer. */ apic_write(APIC_TMICT, (unsigned long)apic_tmict); - return 1; + return apic_tmict || !timeout; } fastcall void smp_apic_timer_interrupt(struct cpu_user_regs * regs) diff --git a/xen/arch/x86/hpet.c b/xen/arch/x86/hpet.c index 3800662624..c775aaa486 100644 --- a/xen/arch/x86/hpet.c +++ b/xen/arch/x86/hpet.c @@ -642,6 +642,8 @@ void hpet_broadcast_enter(void) if ( hpet_attach_channel ) hpet_attach_channel(cpu, ch); + /* Cancel any outstanding LAPIC timer event and disable interrupts. */ + reprogram_timer(0); disable_APIC_timer(); cpu_set(cpu, ch->cpumask); @@ -664,11 +666,8 @@ void hpet_broadcast_exit(void) if ( cpu_test_and_clear(cpu, ch->cpumask) ) { - /* Cancel any outstanding LAPIC event and re-enable interrupts. */ - reprogram_timer(0); - enable_APIC_timer(); - /* Reprogram the deadline; trigger timer work now if it has passed. */ + enable_APIC_timer(); if ( !reprogram_timer(per_cpu(timer_deadline, cpu)) ) raise_softirq(TIMER_SOFTIRQ); diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c index 6d1d7b9e71..83057f91f5 100644 --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -667,10 +667,17 @@ static int vlapic_write(struct vcpu *v, unsigned long address, case APIC_TMICT: { - uint64_t period = (uint64_t)APIC_BUS_CYCLE_NS * - (uint32_t)val * vlapic->hw.timer_divisor; + uint64_t period; vlapic_set_reg(vlapic, APIC_TMICT, val); + if ( val == 0 ) + { + destroy_periodic_time(&vlapic->pt); + break; + } + + period = ((uint64_t)APIC_BUS_CYCLE_NS * + (uint32_t)val * vlapic->hw.timer_divisor); create_periodic_time(current, &vlapic->pt, period, vlapic_lvtt_period(vlapic) ? period : 0, vlapic->pt.irq, vlapic_pt_cb,